package com.xjy.rpc.register.impl;

import com.alibaba.fastjson.JSON;
import com.xjy.rpc.bean.BeanDefinition;
import com.xjy.rpc.bean.ServiceCount;
import com.xjy.rpc.config.Constant;
import com.xjy.rpc.config.RpcProperties;
import com.xjy.rpc.register.IRegisterService;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description: redis注册具体实现
 * @author XJY
 * @date 2024/10/10 14:01
 * @version 1.0
 */
@Slf4j
public class RegisterServiceImpl implements IRegisterService {

    private JedisPool jedisPool;

    private RpcProperties rpcProperties;


    public RegisterServiceImpl(){
        //TODO 后续新增 注册中心密码 也就是redis的密码
        RpcProperties properties = RpcProperties.getInstance();
        String registerAddress = properties.getRegisterAddress();
        String[] split = registerAddress.split(":");
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(10);
        jedisPoolConfig.setMaxIdle(5);
        jedisPool = new JedisPool(jedisPoolConfig,split[0],Integer.parseInt(split[1]));
        rpcProperties = RpcProperties.getRpcProperties();
    }

    @Override
    public void  register(BeanDefinition beanDefinition) {
        //把接口的描述注册到redis中，并设置过期时间
        Jedis jedis = getJedis();
        //TODO 考虑 hash结构吧
        if (!jedis.exists(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName())){
            //如果keySet为空，说明是第一次注册，redis缓存计数器 key：serviceName value ：0/ip/port ip+port用来判断是不是同一服务
            //jedis.set(beanDefinition.getServiceName(),("1"+"/"+beanDefinition.getIp()+"/"+beanDefinition.getPort()));
            jedis.hset(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName(),beanDefinition.getIp() + "/" + beanDefinition.getPort(),"0");
            Long count = jedis.hlen(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName());
            beanDefinition.setServiceCount(count.intValue());
        } else {
            /*String s = jedis.get(beanDefinition.getServiceName());
            String[] split = s.split("/");
            if (split != null && split.length == 3){
                //ip和端口一致，是同一服务
                if (split[1].equals(beanDefinition.getIp()) && split[2].equals(String.valueOf(beanDefinition.getPort()))){
                    beanDefinition.setServiceCount(Integer.valueOf(split[0]));
                } else {
                    //ip或端口不一致，不是同一服务,先改缓存，再从缓存获取count值，这两步要加锁
                    synchronized (RegisterServiceImpl.class) {
                        jedis.del(beanDefinition.getServiceName());
                        Integer count = Integer.valueOf(split[0]);
                        count = count + 1;
                        jedis.set(beanDefinition.getServiceName(),(count+"/"+beanDefinition.getIp()+"/"+beanDefinition.getPort()));
                        String s1 = jedis.get(beanDefinition.getServiceName());
                        String[] split1 = s1.split("/");
                        beanDefinition.setServiceCount(Integer.valueOf(split1[0]));
                    }
                }
            }*/
            Map<String, String> servers = jedis.hgetAll(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName());
            //Long count = jedis.hlen(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName());
            if (servers != null && servers.size() > 0){
                //判断是否是新服务 ip 和 port 都相同
                Set<String> keySet = servers.keySet();
                Collection<String> values = servers.values();
                if (!keySet.contains(beanDefinition.getIp() + "/" + beanDefinition.getPort())){
                    jedis.hset(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName(),beanDefinition.getIp() + "/" + beanDefinition.getPort(),"0");
                }
                Long count = jedis.hlen(Constant.REDIS_PROVIDER_PRE + beanDefinition.getServiceName());
                beanDefinition.setServiceCount(count.intValue());

            }


        }
        String lua = "redis.call('HSET', KEYS[1], KEYS[2],ARGV[1])\n" +
                "redis.call('EXPIRE', KEYS[1],ARGV[2])";
        //组装key,value
        ArrayList<String> keys = new ArrayList<>();
        ArrayList<String> values = new ArrayList<>();
        //service/1 service/3
        keys.add(beanDefinition.getServiceName() + File.separator + beanDefinition.getServiceCount());
        keys.add(beanDefinition.getMethodName());
        values.add(JSON.toJSONString(beanDefinition));
        //TODO 过期时间改成配置文件读取
        long timeSecond = getTimeSecond(rpcProperties.getExpireTime(),rpcProperties.getTimeUnit());
        values.add(String.valueOf(timeSecond));
        try {
            jedis.eval(lua,keys,values);
        } catch (Exception e) {
            log.error("{}->{}服务注册失败",beanDefinition.getServiceName(),beanDefinition.getMethodName());
            throw new RuntimeException(e);
        }
    }

    private long getTimeSecond(String expireTime,Object timeUnit) {
        TimeUnit unit = null;
        if (timeUnit instanceof String) {
            unit = TimeUnit.valueOf((String) timeUnit);
        } else if (timeUnit instanceof TimeUnit){
            unit = (TimeUnit) timeUnit;
        }
        if (unit == null){
            throw new NullPointerException("no unit enum for TimeUnit");
        }
        long timeSecond = unit.toSeconds(Long.valueOf(expireTime));
        return timeSecond;
    }

    @Override
    public void register(BeanDefinition beanDefinition, Map<String, Object> config) {
        //把接口的描述注册到redis中，并设置过期时间
        Jedis jedis = getJedis();
        String lua = "redis.call('HSET', KEYS[1], KEYS[2],ARGV[1])\n" +
                "redis.call('EXPIRE', KEYS[1],ARGV[2])";
        //组装key,value
        ArrayList<String> keys = new ArrayList<>();
        ArrayList<String> values = new ArrayList<>();

        keys.add(beanDefinition.getServiceName());
        keys.add(beanDefinition.getMethodName());
        values.add(JSON.toJSONString(beanDefinition));
        //TODO 这个改成配置
        long timeSecond = getTimeSecond(String.valueOf(config.get(Constant.EXPIRE_TIME)), config.get(Constant.TIME_UNIT));
        values.add(String.valueOf(timeSecond));
        try {
            jedis.eval(lua,keys,values);
        } catch (Exception e) {
            log.error("{}->{}服务注册失败",beanDefinition.getServiceName(),beanDefinition.getMethodName());
            throw new RuntimeException(e);
        }
    }

    private Jedis getJedis(){
        if (jedisPool == null) throw new NoSuchElementException("jedisPool");
        return jedisPool.getResource();
    }


    @Override
    public void unRegister(String serviceName) {
        Jedis jedis = getJedis();
        Set<String> keys = jedis.keys(serviceName + File.separator + File.separator + "*");
        if (keys != null && keys.size() > 0){
            for (String key : keys) {
                jedis.del(key);
            }
        }
        log.info("服务全部下线成功");
    }

    @Override
    public void unRegister(String serviceName, String methodName) {
        Jedis jedis = getJedis();
        jedis.hdel(serviceName,methodName);
        log.info("{}->{}服务下线成功",serviceName,methodName);
    }

    @Override
    public List<BeanDefinition> discoveries(String serviceName) {
        Jedis jedis = getJedis();
        return null;
    }

    @Override
    public BeanDefinition discovery(String serviceName, String methodName) {
        Jedis jedis = getJedis();
        if (jedis.hexists(serviceName,methodName)){
            return JSON.parseObject(jedis.hget(serviceName,methodName),BeanDefinition.class);
        }
        return null;
    }

    @Override
    public void destroy() {

    }

    //测试lua脚本
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //lua脚本结构  redis.call('命令名',固定参数,可变参数) 要不要参数根据命令来选择
        String lua = "redis.call('HSET', KEYS[1], KEYS[2],ARGV[1])\n" +
                "redis.call('EXPIRE', KEYS[1],ARGV[2])";
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setServiceName("rpc");
        ArrayList<String> key = new ArrayList<>();
        key.add("serviceName");
        key.add("methodName");
        ArrayList<String> value = new ArrayList<>();
        value.add(JSON.toJSONString(beanDefinition));
        value.add(String.valueOf(100));
        //api参数  脚本，keylist，valuelist
        jedis.eval(lua,key,value);
        jedis.del("xjy-rpc");
    }
}
